package com.biubiushop.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.request.AlipayFundTransToaccountTransferRequest;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.response.AlipayFundTransToaccountTransferResponse;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.biubiushop.constant.HttpUrlConstant;
import com.biubiushop.constant.ShouQianbaConstant;
import com.biubiushop.constant.WxInfoConstand;
import com.biubiushop.entity.Equipment;
import com.biubiushop.entity.Orders;
import com.biubiushop.exception.ServiceException;
import com.biubiushop.repository.IEquipmentRepository;
import com.biubiushop.repository.IOrdersRepository;
import com.biubiushop.service.IOrdersService;
import com.biubiushop.socket.nio.SocketChannelClient;
import com.biubiushop.util.ALiPayUtil;
import com.biubiushop.util.HttpUtil;
import com.biubiushop.util.JsonUtil;
import com.biubiushop.vo.OrdersVO;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

@Service
public class OrdersServiceImpl implements IOrdersService {

    private static final Log log = LogFactory.get();

    private static final String SING_TYPE = "MD5";

    @Resource
    RedisTemplate<String, Object> redisTemplate;

    @Resource
    IOrdersRepository ordersRepository;

    @Resource
    HttpServletRequest request;

    @Resource
    IEquipmentRepository equipmentRepository;

    @Override
    public Serializable createOrder(String equipmentId, String commodityId, BigDecimal amoutMoney, Integer buyLength, String payway) {
        Equipment equipment = equipmentRepository.getOne(equipmentId);
        if (buyLength > equipment.getOverLength()) {
            throw new ServiceException("纸巾机剩余纸巾不足");
        }
        Orders order = new Orders();
//        order.setUserId(userId);
        order.setBuyLength(buyLength);
        order.setCommodityId(commodityId);
        order.setEquipmentId(equipmentId);
        order.setAmoutMoney(amoutMoney);
        order.setAddress(equipment.getAddress());
        order.setStatus(0);
        order.setCreateTime(new Date());
        ordersRepository.save(order);

        String url = startShouQianBaPay(order, payway);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("equipmentId", equipmentId);
        jsonObject.put("orderId", order.getId());
        jsonObject.put("url", url);
        return jsonObject;
    }

    /**
     * 发起收钱吧支付
     *
     * @param order
     * @return
     */
    private String startShouQianBaPay(Orders order, String payway) {
        String terminal_sn = ShouQianbaConstant.terminal_sn;
        String terminalKey = ShouQianbaConstant.terminal_key;
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("terminal_sn", terminal_sn);                 //终端号
        jsonObject.put("client_sn", order.getId());                 //商户系统订单号,必须在商户系统内唯一；且长度不超过64字节
        jsonObject.put("total_amount", moneyFormat(order.getAmoutMoney()));    //交易总金额,以分为单位
        jsonObject.put("payway", payway);                              //支付方式
        jsonObject.put("subject", "ShareTissue");                      //交易简介
        jsonObject.put("operator", "GZBBWL");                       //门店操作员
        jsonObject.put("return_url", ShouQianbaConstant.return_url);                           //页面跳转同步通知页面路径
        jsonObject.put("description", "ShareTissue");                           //交易详情

        return getShouQianBaUrl(jsonObject, terminalKey);
    }

    /**
     * 收钱吧web api pro支付签名并拼接参数
     *
     * @param jsonObject
     * @param terminal_key
     * @return
     */
    private static String getShouQianBaUrl(JSONObject jsonObject, String terminal_key) {
        // 根据ASCII字典序排序
        Set<String> set = jsonObject.keySet();
        LinkedList<String> list = new LinkedList<>(set);
        Collections.sort(list);
        // 拼接参数
        StringBuilder sb = new StringBuilder();
        for (String key : list) {
            sb.append(key + "=" + jsonObject.get(key) + "&");
        }
        // 拼接api密钥
        String stringSign = sb + "key=" + terminal_key;
        //获取签名
        String sign = DigestUtils.md5DigestAsHex(stringSign.getBytes()).toUpperCase();

        String url = HttpUrlConstant.SHOUQIANBA_PAY + sb + "sign=" + sign;
        log.info("支付跳转请求为" + url);
        return url;
    }


    @Override
    public Serializable startWxMiniPay(String orderId, String amoutMoney) throws Exception {

        JSONObject json = new JSONObject(true);
        long timeStamp = System.currentTimeMillis() / 1000;
        String nonceStr = RandomUtil.randomString(32);

        json.put(WxInfoConstand.APP_ID_KEY, WxInfoConstand.APP_ID);
        json.put(WxInfoConstand.MCH_ID_KEY, "");
        json.put(WxInfoConstand.NONCE_STR_KEY, nonceStr);
        json.put(WxInfoConstand.BODY_KEY, "方便共享纸巾");
        json.put(WxInfoConstand.OUT_TRADE_NO_KEY, orderId);
        json.put(WxInfoConstand.TOTAL_FEE_KEY, amoutMoney);
        json.put(WxInfoConstand.SPBILL_CREATE_IP_KEY, request.getRemoteAddr());
        json.put(WxInfoConstand.NOTIFY_URL_KEY, HttpUrlConstant.WX_MINI_NOTIFY_URL);
        json.put(WxInfoConstand.TRADE_TYPE_KEY, WxInfoConstand.WX_MINI_TRADE_TYPE);
        json.put(WxInfoConstand.TIMESTAMP_KEY, timeStamp);

        JSONObject signObj = new JSONObject();
        signObj.put(WxInfoConstand.APP_ID_KEY, WxInfoConstand.APP_ID);
        signObj.put(WxInfoConstand.TIMESTAMP_KEY, timeStamp);
        signObj.put(WxInfoConstand.NONCE_STR_KEY, nonceStr);
        signObj.put(WxInfoConstand.SIGNTYPE_KEY, SING_TYPE);

        // 小程序调起支付数据签名字段列表：https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=3#
        String sign = getWxMiniSign(signObj);
        json.put(WxInfoConstand.SIGN_KEY, sign);

        // 转xml
        String xmlStr = JsonUtil.jsonToXml(json.toJSONString(), false);

        String xmlResult = HttpUtil.doPost(HttpUrlConstant.POST_UNIFIED_ORDER, xmlStr, HttpUtil.XML_ACCEPT);

        // xml转json
        JSONObject obj = JSONObject.parseObject(JsonUtil.xmlToJSON(xmlResult));

        String resultCode = obj.getString("result_code");
        // 二次签名
        if (StrUtil.isNotEmpty(resultCode) && WxInfoConstand.SUCCESS.equals(resultCode)) {
            JSONObject twiceSignObj = new JSONObject(true);
            String prepayId = obj.getString("prepay_id");
            twiceSignObj.put(WxInfoConstand.APP_ID_KEY, WxInfoConstand.APP_ID);
            twiceSignObj.put(WxInfoConstand.TIMESTAMP_KEY, timeStamp);
            twiceSignObj.put(WxInfoConstand.NONCE_STR_KEY, nonceStr);
            twiceSignObj.put(WxInfoConstand.PACKAGE_KEY, "prepay_id=" + prepayId);
            twiceSignObj.put(WxInfoConstand.SIGNTYPE_KEY, SING_TYPE);

            // 生成新的签名
            String twiceSign = getWxMiniSign(twiceSignObj);

            // 将签名存入redis,用作回调签名校验
            redisTemplate.opsForValue().set(orderId, twiceSign);

            twiceSignObj.put(WxInfoConstand.TWICE_PAY_SIGN_KEY, twiceSign);

            twiceSignObj.remove(WxInfoConstand.APP_ID_KEY);

            return twiceSignObj;
        }
        String errorMsg = obj.get("return_msg").toString();
        log.error("=================微信支付统一下单错误:" + errorMsg + "====================");
        return errorMsg;
    }

    @Override
    public Serializable wxMiniNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream()));
        String line;
        StringBuilder sb = new StringBuilder();
        while ((line = br.readLine()) != null) {
            sb.append(line);
        }
        br.close();
        // sb为微信返回的xml
        String notityXml = sb.toString();
        String resXml;

        String jsonStr = JsonUtil.xmlToJSON(notityXml);
        JSONObject json = JSONObject.parseObject(jsonStr);

        boolean isSucceed = false;
        String orderId = "";
        String returnCode = json.getString("return_code");
        if ("SUCCESS".equals(returnCode)) {
            orderId = json.getString("out_trade_no");
            // 从redis中取出订单对应的签名,进行比对
            Object obj = redisTemplate.opsForValue().get(orderId);
            if (ObjectUtil.isNull(obj)) {
                throw new ServiceException("redis中不存在此订单");
            }
            String redisSign = obj.toString();
            if (json.getString("sign").equals(redisSign)) {
                isSucceed = true;
            }
        }

        if (isSucceed) {
            // 将订单状态改为已支付
            Orders order = ordersRepository.getById(orderId);
            order.setStatus(1);
            ordersRepository.save(order);

            // 通知微信服务器已经支付成功
            resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                    + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
        } else {
            resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                    + "<return_msg><![CDATA[参数格式校验错误]]></return_msg>" + "</xml> ";
        }
        log.info("========================微信支付回调数据结束===============================");

        BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
        out.write(resXml.getBytes());
        out.flush();
        out.close();
        return null;
    }

    private String getWxMiniSign(JSONObject jsonObject) {
        // 根据ASCII字典序排序
        Set<String> set = jsonObject.keySet();
        LinkedList<String> list = new LinkedList<>(set);
        Collections.sort(list);
        // 拼接参数
        StringBuilder sb = new StringBuilder();
        for (String key : list) {
            sb.append(key + "=" + jsonObject.get(key) + "&");
        }
        // 拼接api密钥
        String stringSign = sb + "key=" + WxInfoConstand.APP_SECRET;

        return DigestUtils.md5DigestAsHex(stringSign.getBytes()).toUpperCase();
    }

    @Override
    public String startALiPay(String orderId, String totalAmount) throws Exception {
        //实例化客户端
        AlipayClient alipayClient = ALiPayUtil.getInstance();
        //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.app.pay
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
        //SDK已经封装掉了公共参数，这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        model.setBody("我是测试数据");
        model.setSubject("方便App支付测试");
        model.setOutTradeNo(orderId);
        model.setTimeoutExpress("30m");
        model.setTotalAmount(totalAmount);
        model.setProductCode("QUICK_MSECURITY_PAY");
        request.setBizModel(model);
        //服务端异步回调地址
//        request.setNotifyUrl("http://www.biubiushop-test.com/order/v1/aLiNotify");
        //客户端支付跳转页面
//        request.setReturnUrl("");
        String orderString = null;
        try {
            //这里和普通的接口调用不同，使用的是sdkExecute
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
            //就是orderString 可以直接给客户端请求，无需再做处理。
            orderString = response.getBody();
            log.info(orderString);
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        return orderString;
    }

    @Override
    public Serializable startAppTrans(String outBizNo) throws Exception {
        //实例化客户端
        AlipayClient alipayClient = ALiPayUtil.getInstance();
        AlipayFundTransToaccountTransferRequest request = new AlipayFundTransToaccountTransferRequest();
        request.setBizContent("{" +
                "    \"out_biz_no\":" + outBizNo + "," +
                "    \"payee_type\":\"ALIPAY_LOGONID\"," +
//                "    \"payee_account\":" + account + "," +
//                "    \"amount\":" + amount + "," +
                "    \"remark\":\"方便app提现\"," +
                "  }");
        AlipayFundTransToaccountTransferResponse response = alipayClient.execute(request);
        if (response.isSuccess()) {
            System.out.println("调用成功");
        } else {
            System.out.println("调用失败");
        }
        return null;
    }

    @Override
    public Serializable aLiNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public List<OrdersVO> getOrderList(String userId) throws Exception {
        List<Orders> ordersList = ordersRepository.getByUserId(userId);
        List<OrdersVO> voList = null;
        if (ObjectUtil.isNotEmpty(ordersList) && ordersList.size() > 0) {
            voList = CollUtil.newArrayList();
            for (Orders order : ordersList
            ) {
                OrdersVO vo = new OrdersVO();
                BeanUtil.copyProperties(order, vo);
                voList.add(vo);
            }
        }
        return voList;
    }

    @Override
    public void execute(String equipmentId, String orderId) throws IOException {
        Orders order = ordersRepository.getById(orderId);
        Equipment equipment = equipmentRepository.getOne(equipmentId);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("instructions", 1);
        jsonObject.put("length", order.getBuyLength());
        jsonObject.put("id", equipment.getSocketId());
        jsonObject.put("orderId", orderId);

        //第一版的设备
        if (("4028824f6d819f4f016db3b07a4c0224").equals(equipmentId)) {
            String url = " "+order.getBuyLength();
            String result;
            JSONObject obj;
            try {
                result = HttpUtil.doGet(url);
                obj = JSONObject.parseObject(result);
                if ("200".equals(obj.getString("code"))) {
                    order.setStatus(2);
                }else {
                    order.setStatus(4);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            order.setStatus(1);
            SocketChannelClient.start(jsonObject);
        }
        ordersRepository.save(order);
    }

    /**
     * 将金额转换为以分为单位的整数
     *
     * @param sourceMoney 需要转换的金额
     * @return
     */
    public static Integer moneyFormat(BigDecimal sourceMoney) {
        String str = sourceMoney.multiply(new BigDecimal(100)).stripTrailingZeros().toString();
        Double resultMoney = Double.valueOf(str);
        return resultMoney.intValue();
    }


    public static void main(String[] args) {
//        String str = "{" +
//                "    \"out_biz_no\":\"3142321423432\"," +
//                "    \"payee_type\":\"ALIPAY_LOGONID\"," +
//                "    \"payee_account\":\"abc@sina.com\"," +
//                "    \"amount\":\"12.23\"," +
//                "    \"payer_show_name\":\"上海交通卡退款\"," +
//                "    \"payee_real_name\":\"张三\"," +
//                "    \"remark\":\"转账备注\"," +
//                "  }";
//        JSONObject jsonObject = JSONObject.parseObject(str);
//        System.out.println(jsonObject);
//        Map map = Maps.newHashMap();
//        map.put("1",1);
//        map.put("2","adada");
    }
}
